package com.njy.config;

import com.njy.decorator.AsyncDecorator;
import lombok.Data;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.spi.MDCAdapter;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.task.TaskDecorator;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

@Component
@Data
public class JdkThreadPoolExecutor implements InitializingBean {

    private final static Logger logger = LoggerFactory.getLogger(JdkThreadPoolExecutor.class);

    @Autowired
    private ThreadPoolParam p;

    @Autowired
    private AsyncDecorator asyncDecorator;

    /**
     * 要将对象B注入到对象A，那么首先就必须得生成对象A和对象B，才能执行注入。
     * 所以，如果一个类A中有个成员变量b被@Autowried注解，那么@Autowired注入是发生在A的构造方法执行完之后的。
     * 如果想在生成对象时完成某些初始化操作，而偏偏这些初始化操作又依赖于依赖注入，那么久无法在构造函数中实现
     * 通俗讲 在A类中用@Autowried注入B类，想对b做一些操作就是用@PostConstruct自动调用
     * 因为执行顺序如下：Constructor >> @Autowired >> @PostConstruct
     * 此时a的构造方法已经执行完了，a执行构造方法时b还没被注入，不能再构造方法中操作b对象，所以使用 @PostConstruct
     */
/*    @PostConstruct
    private void init() {

    }*/

    public void execute(Runnable r) {
        p.getThreadPool().execute(asyncDecorator.decorate(r));
    };

    /**
     * bean初始化的时候操作内容
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
       /* logger.info("corePoolSize Number: "+p.getCorePoolSize());
        logger.info("maximumPoolSize Number: "+p.getMaximumPoolSize());
        logger.info("keepAliveTime Number: "+p.getKeepAliveTime());*/
        p.setThreadFactory(new SimpleThreadFactory(p.getThreadName()));
        p.setThreadPool(new ThreadPoolExecutor(p.getCorePoolSize(),
                p.getMaximumPoolSize(), p.getKeepAliveTime(), TimeUnit.SECONDS,
                p.getWorkQueue(), p.getThreadFactory(), p.getHandler()));
    }

    private class SimpleThreadFactory implements ThreadFactory {
        final AtomicInteger atomicInteger = new AtomicInteger(0);
        final String threadName;
        final ThreadGroup threadGroup;
        public SimpleThreadFactory(String threadName) {
            this.threadName = threadName;
            SecurityManager securityManager = System.getSecurityManager();;
            if (securityManager !=null )
                threadGroup = securityManager.getThreadGroup();
            else
                threadGroup = new ThreadGroup("DEFAULT_THREAD_GROUP");
        }
        @Override
        public Thread newThread(Runnable r) {
            return new Thread(threadGroup,asyncDecorator.decorate(r),threadName+atomicInteger.getAndIncrement(),0L);
        }
    }
}
